package kvraft

import (
	"6.824/labrpc"
	"sync"
	"time"
)
import "crypto/rand"
import "math/big"

type Clerk struct {
	servers []*labrpc.ClientEnd
	// You will have to modify this struct.
	mu        sync.Mutex
	ID        int64
	RequestID int
	leaderID  int
}

func (ck *Clerk) getRequestID() (RequestID int) {
	ck.mu.Lock()
	ck.RequestID++
	RequestID = ck.RequestID
	ck.mu.Unlock()
	return
}

func (ck *Clerk) getLeaderID() (leaderID int) {
	ck.mu.Lock()
	leaderID = ck.leaderID
	ck.mu.Unlock()
	rep := GetReply{}
	ok := ck.servers[leaderID].Call("KVServer.Get", &GetArgs{}, &rep)
	if ok && rep.Err != ErrWrongLeader {
		return
	}
	//不断向每个server发送请求确认是否是leader，一旦发现leader则退出所有子线程
	findLeader := make(chan int, 0)
	exit := make(chan struct{}, len(ck.servers))
	defer func() {
		for i := 0; i < len(ck.servers); i++ {
			exit <- struct{}{}
		}
	}()
	for id, server := range ck.servers {
		go func(id int, server *labrpc.ClientEnd) {
			for {
				select {
				case <-exit:
					return
				case <-time.After(time.Millisecond):
					reply := GetReply{}
					ok := server.Call("KVServer.Get", &GetArgs{}, &reply)
					if ok {
						if reply.Err != ErrWrongLeader {
							findLeader <- id
						}
					}
					time.Sleep(time.Second)
				}
			}
		}(id, server)
	}
	leaderID = <-findLeader
	ck.mu.Lock()
	ck.leaderID = leaderID
	ck.mu.Unlock()
	DPrintf("ok! now %v is the leader!", leaderID)
	return
}

func nrand() int64 {
	max := big.NewInt(int64(1) << 62)
	bigx, _ := rand.Int(rand.Reader, max)
	x := bigx.Int64()
	return x
}

func MakeClerk(servers []*labrpc.ClientEnd) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	ck.ID = nrand()
	DPrintf("make Clerk")
	// You'll have to add code here.
	return ck
}

// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.Get", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) Get(key string) string {
	// You will have to modify this function.
	//DPrintf("Get key %v", key)
	var rep GetReply
	ok := false
	RequestID := ck.getRequestID()
	ck.mu.Lock()
	leader := ck.leaderID
	ck.mu.Unlock()
	//超时或者发现leader错误则重发
	for !ok || rep.Err == ErrWrongLeader {
		if rep.Err == ErrWrongLeader {
			leader = ck.getLeaderID()
		}
		rep = GetReply{}
		ok = ck.servers[leader].Call("KVServer.Get",
			&GetArgs{Key: key, RequestID: RequestID, ClientID: ck.ID},
			&rep)
	}
	if rep.Err == ErrNoKey {
		return ""
	} else {
		return rep.Value
	}

}

// shared by Put and Append.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.PutAppend", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) PutAppend(key string, value string, op string) {
	// You will have to modify this function.
	//DPrintf("putAppend key %v val %v op %v", key, value, op)
	var rep PutAppendReply
	ok := false
	ck.mu.Lock()
	leader := ck.leaderID
	ck.mu.Unlock()
	RequestID := ck.getRequestID()
	//超时或者发现leader错误则重发
	for !ok || rep.Err == ErrWrongLeader {
		if rep.Err == ErrWrongLeader {
			leader = ck.getLeaderID()
		}
		rep = PutAppendReply{}
		ok = ck.servers[leader].Call("KVServer.PutAppend",
			&PutAppendArgs{Key: key, Value: value, Op: op, RequestID: RequestID, ClientID: ck.ID},
			&rep)
	}
	return
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, "Append")
}
